package roborally.model;

import be.kuleuven.cs.som.annotate.*;

/**
 * A class for dealing with repair kits involving an amount of energy and a weight.
 * 
* @invar	The amount of energy of each repair kit must be a legal amount of energy for a repair kit.
 *          | canHaveAsEnergy(getEnergy())       
 * 
 * @version	1.0
 * @author  Pieter Robberechts	(1e Bach Informatica)
 * @author	Jeroen Sanders		(1e Bach Informatica)
 *
 */
public class RepairKit extends Item implements IEnergyHolder {
	
	/**
	 * Initialize this new repair kit with a given amount of energy and a given weight.
	 * 
	 * @param	energyAmount
	 * 			The amount of energy for this new battery.
	 * @param	weight
	 * 			The weight for this new battery.
	 * @pre		...
	 * 			| canHaveAsEnergy(energyAmount)
	 * @effect	...
	 * 			| super(weight)
	 * @effect	...
	 * 			| setEnergy(energyAmount)
	 */
	public RepairKit(EnergyAmount energyAmount, Weight weight) {
		super(weight);
		assert(canHaveAsEnergy(energyAmount));
		setEnergy(energyAmount);
	}

	/**
	 * Return a standard weight for a repair kit (in grams).
	 */
	@Override @Basic @Raw
	public Weight getStandardWeight() {
		return new Weight(100);
	}
	
	/**
	 * Variable registering the amount of energy stored in this repair kit.
	 */
	private EnergyAmount energyAmount;
	
	/**
	 * Return the amount of energy of this repair kit.
	 */
	@Override @Basic @Raw
	public EnergyAmount getEnergy() {
		return energyAmount;
	}
	
	/**
	 * Set the amount of energy of this repair kit to the given amount of energy.
	 * 
	 * @param 	energyAmount
	 * 			The amount of energy for this repair kit.
	 * @pre		...
	 * 			| canHaveAsEnergy(energyAmount)
     * @post   	...
     *       	| new.getEnergy().isIdenticalTo(energyAmount)
	 */
	@Model @Raw
	private void setEnergy(EnergyAmount energyAmount) {
		assert canHaveAsEnergy(energyAmount);
		this.energyAmount = energyAmount;
	}
	
	/**
	 * Check whether the given amount of energy is a valid amount of energy for a battery.
	 * 
	 * @param 	ws
	 * 			The amount of energy to check.
	 * @return	...
	 * 			| result == ! ws.moreThan(getMaxEnergy())
	 */
	@Raw @Override
	public boolean canHaveAsEnergy(EnergyAmount ws) {
		return (! ws.moreThan(getMaxEnergy()));
	}

	/**
	 * Return the maximal amount of energy any repair kit can store.
	 * 
	 * @return	...
	 * 			| result == new EnergyAmount(Double.MAX_VALUE)
	 */
	@Immutable @Basic
	private EnergyAmount getMaxEnergy(){
		return new EnergyAmount(Double.MAX_VALUE);
	}
	
	/**
	 * Recharges a repair kit with a given amount of energy.
	 * 
	 * @param 	amount
	 * 		  	The amount of energy to add.
	 * @pre		...
	 * 			| amount != null
	 * @pre 	...
	 * 			| canHaveAsEnergy( getEnergy().add(amount) )
	 * @effect	...
	 * 			| setEnergy( getEnergy().add(amount) )
	 */
	@Override
	public void recharge(EnergyAmount amount) {
		assert (amount != null);
		assert canHaveAsEnergy(getEnergy().add(amount));
		setEnergy(getEnergy().add(amount));
	}

	/**
	 * Transfer energy from a repair kit to another energy holder.
	 * 
	 * @param	other
	 * 			The energy holder to transfer an amount of energy to.
	 * @param	amount
	 * 			The amount of energy to transfer.
	 * @pre		...
	 * 			| other != null
	 * @pre		...
	 * 			| !other.isTerminated()
	 * @pre		...
	 * 			| amount != null
	 * @pre		...
	 * 			| other.canHaveAsEnergy(getEnergy().add(amount))
	 * @pre		...
	 * 			| this.canHaveAsEnergy(getEnergy().subtract(amount))
	 * @effect	...
	 * 			| setEnergy(getEnergy().subtract(amount))
	 * @effect	...
	 * 			| other.recharge(amount)
	 */
	@Override
	public void transferEnergy(IEnergyHolder other, EnergyAmount amount) {
		assert (other != null);
		assert !other.isTerminated();
		assert (amount != null);
		assert other.canHaveAsEnergy(getEnergy().add(amount));
		assert this.canHaveAsEnergy(getEnergy().subtract(amount));
		setEnergy(getEnergy().subtract(amount));
		other.recharge(amount);
	}
	
	/**
	 * Repair a given robot with the energy from this repair kit.
	 * 	
	 * @param	robot
	 * 			The robot which should be repaired with the energy of this repair kit.
	 * @pre		...
	 * 			| robot != null
	 * @pre		...
	 * 			| ! this.isTerminated() && ! robot.isTerminated()
	 * @effect	...
	 * 			| if 2*robot.getMaxMaxEnergy().subtract(robot.getMaxEnergy()).getAmountInUnits()
	 * 			|	 < getEnergy().getAmountInUnits()
	 * 			|	then robot.repairRobot(robot.getMaxMaxEnergy().subtract(robot.getMaxEnergy())) &&
	 * 			|		 setEnergy(new EnergyAmount(getEnergy().getAmountInUnits()-2*
	 * 			|			robot.getMaxMaxEnergy().subtract(robot.getMaxEnergy())))
	 * 			| else
	 * 			|	 robot.repairRobot(new EnergyAmount(getEnergy().getAmountInUnits()/2)) &&
	 * 			|	 robot.drop(this) && terminate()
	 */
	@Override
	public void use(Robot robot) {
		assert(robot != null);
		assert(! this.isTerminated() && ! robot.isTerminated());
		EnergyAmount repair = robot.getMaxMaxEnergy().subtract(robot.getMaxEnergy());
		if(2*repair.getAmountInUnits() < getEnergy().getAmountInUnits()) {
			robot.repairRobot(repair);
			setEnergy(getEnergy().subtract(repair).subtract(repair));
		} else {
			robot.repairRobot(new EnergyAmount(getEnergy().getAmountInUnits()/2));
			robot.drop(this);
			terminate();
		}
	}
	
	/**
	 * Executes the effects of a laser on the repair kit.
	 * 
	 * @effect	...
	 * 			| if canHaveAsEnergy(getEnergy().add(new EnergyAmount(500)))
	 * 			|	then recharge(new EnergyAmount(500))
	 */
	@Override
	public void damageByLaser() {
		if(canHaveAsEnergy(getEnergy().add(new EnergyAmount(500)))){
			recharge(new EnergyAmount(500));
		}
	}

    /**
     * Return a textual representation of a repair kit.
     * 
     * @return      ...
     *                      | if( getPosition() != null )
     *                      |       then result == "Repair kit [Energy: " + getEnergy() + ", Weight: " + getWeight() + ", Position: " + getPosition().toString() + "]";
     *                      | else
     *                      |       then result == "Repair kit [Energy: " + getEnergy() + ", Weight " + getWeight() + " ]";
     * @return      ...
     *                      | if isTerminated()
     *                      |       then result == "This repair kit is terminated."
     */
    @Override
    public String toString(){
            if(isTerminated())
                    return "This repair kit is terminated.";
            if(getPosition() != null)
                    return "Repair kit [Energy: " + getEnergy() + ", Weight: " + getWeight() + ", Position: " + getPosition().toString() + "]";
            else
                    return "Repair kit [Energy: " + getEnergy() + ", Weight " + getWeight() + " ]";

    }
}